计算机网络 - 网络架构
目录
目录
| 层级 | 层名称 | 核心机构与企业 | 代表性贡献 |
|---|---|---|---|
| 5 | 应用层 (Application) | IETF, W3C, CERN, NCSA, Qualcomm | IETF/W3C: 制订 HTTP, HTML, DNS 标准 CERN: 发明万维网 (World Wide Web) NCSA: 推出 Mosaic 浏览器 Qualcomm: POP3 电子邮件协议 |
| 4 | 传输层 (Transport) | IETF, UC Berkeley, Google, UMass Amherst | IETF: 推动 TCP, UDP, QUIC 标准化 UC Berkeley: 在 BSD Unix 中实现 TCP/IP Google: 推动 QUIC 协议发展 UMass Amherst: 在网络建模、性能分析和高速协议方面的先驱性研究 |
| 3 | 网络层 (Network) | IETF, DARPA, Stanford, Cisco, UT Austin | DARPA/Stanford: 资助与开发 TCP/IP Cisco: 开发路由协议 (EIGRP, OSPF) UT Austin: 1969年 ARPANET 最初的4个节点之一;参与了历史上首次网络消息传递。 |
| 2 | 链路层 (Data Link) | IEEE, Xerox PARC, Univ. of Cambridge | IEEE: 制订 Ethernet (802.3) 和 Wi-Fi (802.11) 标准 Xerox PARC: 发明 Ethernet (Robert Metcalfe) Univ. of Cambridge: 发明 Cambridge Ring |
| 1 | 物理层 (Physical) | IEEE, ITU, Bell Labs, AT&T | IEEE/ITU: 制订线缆、光纤、无线电等通信标准 Bell Labs/AT&T: 发明晶体管、推动数字传输与 T-carrier 系统 |
一、网络应用架构(Application Architecture)
在开始讨论具体的应用层协议之前,需要先理解网络应用的架构设计。这不是指网络本身的协议栈分层,而是开发者在设计网络应用时所选择的通信组织方式。
主要有两种范式:
| 架构 | 核心思想 | 代表应用 |
|---|---|---|
| 客户端-服务器(Client-Server) | 有一个永远在线的服务器,客户端向它发起请求 | Web、电子邮件、网银 |
| 对等网络(Peer-to-Peer, P2P) | 没有固定服务器,终端之间直接通信 | BitTorrent、区块链、早期 Skype |
注意区分:这里说的是应用层架构,不是网络层的拓扑结构。底层的 Internet 始终是分组交换网络,但在应用层,你可以选择让程序以 C/S 模式还是 P2P 模式运行。
二、客户端-服务器范式(Client-Server Paradigm)
2.1 基本模型
graph TD
Server["服务器\n永远在线 · 固定 IP\n(通常在数据中心)"]
C1["客户端 1\n间歇性连接 · 动态 IP"]
C2["客户端 2\n间歇性连接 · 动态 IP"]
C3["客户端 N\n间歇性连接 · 动态 IP"]
C1 -->|"请求/响应"| Server
C2 -->|"请求/响应"| Server
C3 -->|"请求/响应"| Server
C1 -.-x|"不直接通信"| C2
2.2 服务器端的特征
| 特征 | 说明 |
|---|---|
| 永远在线(Always-on) | 服务器必须 7×24 小时运行,随时等待客户端请求 |
| 固定 IP 地址 | 服务器需要一个永久的、已知的 IP 地址(或域名),以便客户端能找到它 |
| 部署在数据中心 | 为了应对大量并发请求,服务器通常部署在拥有高带宽和冗余电力的数据中心 |
| 可扩展性 | 通过增加服务器数量(水平扩展)来应对增长的请求量 |
2.3 客户端的特征
| 特征 | 说明 |
|---|---|
| 间歇性连接 | 客户端不需要永远在线,需要时才连接 |
| 动态 IP 地址 | 大多数客户端的 IP 由 ISP 动态分配(DHCP),每次联网可能不同 |
| 不直接通信 | 在纯 C/S 模型中,客户端之间不直接通信,所有交互都经过服务器 |
| 发起请求 | 通信总是由客户端发起(服务器被动等待) |
为什么客户端之间不直接通信? 因为客户端的 IP 地址是动态的、不可预测的,而且它们可能在 NAT(网络地址转换)后面,从外部根本无法直接访问。只有服务器拥有固定 IP,所以一切通信都要经过它。
2.4 C/S 模型的可扩展性问题
随着用户数量增长,单台服务器的处理能力终将不够。解决方案:
graph TD
Client1["客户端 1"] --> LB["负载均衡器"]
Client2["客户端 2"] --> LB
Client3["客户端 N"] --> LB
LB --> S1["服务器 1"]
LB --> S2["服务器 2"]
LB --> S3["服务器 N"]
S1 --> DB["共享数据库 / 缓存"]
S2 --> DB
S3 --> DB
| 扩展策略 | 说明 |
|---|---|
| 垂直扩展(Scale Up) | 给单台服务器加 CPU、内存、带宽——简单但有上限 |
| 水平扩展(Scale Out) | 增加服务器数量,用负载均衡器分配请求——理论上可无限扩展 |
| 数据中心集群 | Google、Amazon 等在全球部署多个数据中心,结合 CDN 就近服务用户 |
现实数据:Google 在全球拥有 30+ 个数据中心,仅搜索服务每天就要处理约 85 亿次请求。这种量级只能通过大规模水平扩展实现。
三、P2P 范式(Peer-to-Peer)
3.1 基本模型
graph LR
A["Peer A"] <-->|"数据交换"| B["Peer B"]
A <-->|"数据交换"| C["Peer C"]
A <-->|"数据交换"| D["Peer D"]
B <-->|"数据交换"| C
B <-->|"数据交换"| D
C <-->|"数据交换"| D
没有永远在线的服务器!每个 Peer 既是客户端,也是服务器。
3.2 P2P 的核心特征
| 特征 | 说明 |
|---|---|
| 无专用服务器 | 不存在一个永远在线的中心服务器(或者中心服务器只做极少量的协调工作) |
| Peer 既是客户又是服务者 | 每个节点同时请求服务和提供服务——自扩展(self-scalability) |
| 间歇性连接 | Peer 随时可能上线或下线,IP 地址也是动态的 |
| 管理复杂 | 没有中心化控制,安全性、内容一致性等问题更难处理 |
自扩展性(Self-scalability):这是 P2P 最强大的特性。在 C/S 模型中,用户增加 = 服务器负载增加;但在 P2P 中,每个新用户既带来需求也带来供给(既下载也上传),所以系统的总服务能力随用户增加而自动增长。
3.3 C/S vs P2P 文件分发效率对比
这是一个经典的分析问题。假设一个服务器要把大小为 的文件分发给 个客户端。
C/S 模型下的分发时间下界:
- = 服务器上传带宽
- = 所有客户端中最小的下载带宽
- 服务器必须把文件传 次(每个客户端一份),所以分发时间随 线性增长
P2P 模型下的分发时间下界:
- = 第 个 Peer 的上传带宽
- 关键区别:分母中多了 (所有 Peer 的上传带宽之和),每个新 Peer 都贡献上传能力
xychart-beta
title "文件分发时间 vs 用户数"
x-axis "用户数 N" [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
y-axis "分发时间"
line "C/S(线性增长)" [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
line "P2P(亚线性增长)" [1, 1.3, 1.5, 1.6, 1.7, 1.75, 1.8, 1.83, 1.86, 1.9]
直觉理解:想象一个老师(服务器)要把一份讲义发给全班 50 人。C/S 模式就是老师一个一个复印发放,50 个人要印 50 次。P2P 模式就是老师印 1 份给第一个同学,然后让拿到的同学也帮忙复印——很快每个人都在帮忙复印,分发速度飞快。
3.4 BitTorrent 协议
BitTorrent 是最具代表性的 P2P 文件分发协议。
基本概念
| 术语 | 含义 |
|---|---|
| Torrent | 参与分发同一文件的所有 Peer 的集合 |
| Tracker | 跟踪所有参与 Peer 的服务器(告诉新加入的 Peer "谁有这个文件") |
| Chunk | 文件被分成等大的块(通常 256 KB),以 Chunk 为单位交换 |
| Seed(做种者) | 已拥有完整文件、只上传不下载的 Peer |
| Leecher(吸血者) | 还没下完文件、正在下载的 Peer |
核心机制
1. 最稀有优先(Rarest First)
下载时优先请求在整个 Torrent 中副本数最少的 Chunk。
为什么? 如果某个 Chunk 只有 1 个 Peer 拥有,而那个 Peer 下线了,这个 Chunk 就永远无法获取了。优先请求稀有 Chunk 可以让所有 Chunk 尽快在网络中均匀分布。
2. Tit-for-Tat(以牙还牙)
每个 Peer 优先向给自己上传速度最快的 4 个 Peer 上传数据(称为 unchoked peers),每 10 秒重新评估一次。同时每 30 秒随机选一个 Peer 上传(optimistic unchoking)。
博弈论解读:这是一种激励兼容(incentive-compatible) 设计——你上传得越多,别人就越愿意给你上传。"白嫖"的 Peer 会被"惩罚"(choked),因为没人愿意给不上传的人传数据。这也是为什么有些 BT 客户端强调"做种比率"。
四、混合架构
实际上很多应用采用混合架构——核心功能用 C/S,但某些部分用 P2P。
| 应用 | C/S 部分 | P2P 部分 |
|---|---|---|
| 即时通讯(早期 QQ/微信) | 用户注册、登录、好友列表存储在中心服务器 | 用户之间的消息传输可以是直连的(端到端) |
| Skype(早期) | 用户认证走中心服务器 | 语音/视频数据在用户间直传,超级节点帮助 NAT 穿透 |
| BitTorrent | Tracker 是 C/S 模式(告诉你谁有文件) | 文件传输本身是 P2P |
现代趋势:随着云计算成本下降,很多原本使用 P2P 的应用已经转向 C/S(如 Skype 现在完全由 Microsoft 服务器中继)。但 P2P 在去中心化场景(区块链、IPFS)中依然不可替代。
五、进程通信(Process Communication)
网络应用的本质是不同主机上的进程之间的通信。理解"进程如何通过网络交流"是理解所有应用层协议的基础。
5.1 进程与套接字(Socket)
进程(Process):运行在主机上的程序实例。
在同一台主机上,进程间通信由操作系统管理(如管道、共享内存)。但在不同主机上,进程必须通过网络交换消息。
graph TD
subgraph 主机A["主机 A"]
AppA["应用进程(浏览器)"]
SocketA["Socket"]
TransA["传输层(TCP/UDP)"]
NetA["网络层 ..."]
AppA --> SocketA --> TransA --> NetA
end
subgraph 主机B["主机 B"]
AppB["应用进程(Web 服务器)"]
SocketB["Socket"]
TransB["传输层(TCP/UDP)"]
NetB["网络层 ..."]
AppB --> SocketB --> TransB --> NetB
end
NetA <-->|"互联网"| NetB
套接字(Socket) 是进程与网络之间的接口(门)。
类比:进程是房间里的人,Socket 是房间的门。你把信(数据)交给门口(Socket),邮递员(传输层)帮你送到对面房间的门口。你不需要知道邮递员怎么走的,只管把信塞进门就行。
| 概念 | 解释 |
|---|---|
| Socket | 应用层与传输层之间的 API 接口 |
| 发送方 | 进程把消息推入 Socket |
| 接收方 | 进程从 Socket 拉出消息 |
| 开发者控制 | Socket 以上的应用层逻辑 |
| OS 控制 | Socket 以下的传输层、网络层等 |
5.2 进程寻址(Addressing)
要把消息送到正确的进程,需要两个信息:
| 标识 | 作用 | 例子 |
|---|---|---|
| IP 地址 | 标识哪台主机 | 142.250.80.46(Google 的一个 IP) |
| 端口号(Port Number) | 标识主机上的哪个进程 | 80(HTTP)、443(HTTPS)、25(SMTP) |
类比:IP 地址就像一栋大楼的门牌号,端口号就像楼里的房间号。邮件上不仅要写"XX路123号"(IP),还要写"5楼501室"(端口),快递员才知道送给谁。
知名端口号(Well-known Ports):0-1023 号端口被系统保留给标准服务。
| 端口号 | 协议 | 服务 |
|---|---|---|
| 20, 21 | FTP | 文件传输 |
| 22 | SSH | 安全远程登录 |
| 25 | SMTP | 发送电子邮件 |
| 53 | DNS | 域名解析 |
| 80 | HTTP | 网页浏览 |
| 443 | HTTPS | 加密网页浏览 |
六、应用层协议定义的内容
一个应用层协议需要定义以下几个方面:
| 维度 | 内容 | 例子(HTTP) |
|---|---|---|
| 消息类型 | 有哪些种类的消息 | 请求(Request)、响应(Response) |
| 消息语法(Syntax) | 消息中有哪些字段、如何排列 | 请求行、头部字段、空行、消息体 |
| 消息语义(Semantics) | 各字段的含义 | Content-Type: text/html 表示正文是 HTML |
| 规则(Rules) | 何时、如何发送和响应消息 | 客户端先发请求,服务器收到后回响应 |
协议的开放性:
| 类型 | 说明 | 例子 |
|---|---|---|
| 开放协议 | 由 RFC(Request for Comments)标准文档定义,任何人都能实现 | HTTP、SMTP、DNS |
| 私有协议 | 由公司自行定义,不公开细节 | Skype 早期协议、某些游戏的通信协议 |
RFC 是由 IETF(Internet Engineering Task Force)发布的互联网标准文档。例如 HTTP/1.1 定义在 RFC 2616(后被 RFC 7230-7235 取代),任何开发者都可以据此实现自己的 Web 服务器或浏览器。
七、应用对传输服务的需求
不同的网络应用对传输层有不同的需求,可以从四个维度来分析:
7.1 四个维度
| 维度 | 含义 | 高要求的应用 | 低要求/容忍的应用 |
|---|---|---|---|
| 数据完整性(Data Integrity) | 数据是否能容忍丢失 | 文件传输、网银、电子邮件(零容忍) | 音视频通话(丢几个包人耳察觉不到) |
| 吞吐量(Throughput) | 需要多少最低带宽保障 | 视频流媒体(需要稳定高吞吐) | 电子邮件(多慢都能忍) |
| 时延(Timing / Delay) | 对延迟的敏感程度 | 在线游戏、IP 电话(< 150ms) | 文件下载(延迟无所谓) |
| 安全性(Security) | 需要加密、认证等 | 网银、电子商务 | 公开网页浏览(低要求) |
7.2 带宽敏感 vs 弹性应用
| 类型 | 含义 | 代表应用 |
|---|---|---|
| 带宽敏感型(bandwidth-sensitive / inelastic) | 需要最低带宽保障才能正常工作 | 视频流(720p 至少需要 ~2.5 Mbps)、VoIP |
| 弹性应用(elastic) | 带宽多少都能用,越多越好 | 文件传输、Web 浏览、电子邮件 |
7.3 常见应用的需求总结
| 应用 | 数据完整性 | 吞吐量 | 时延 | 典型传输协议 |
|---|---|---|---|---|
| 文件传输 | 不能丢 | 弹性 | 不敏感 | TCP |
| 电子邮件 | 不能丢 | 弹性 | 不敏感 | TCP |
| Web 浏览 | 不能丢 | 弹性 | 较敏感 | TCP |
| 实时音视频 | 可容忍少量丢失 | 带宽敏感 | 极敏感(< 150ms) | UDP / RTP |
| 在线游戏 | 可容忍少量丢失 | 较低 | 极敏感 | UDP |
| 即时消息 | 不能丢 | 弹性 | 较敏感 | TCP |
八、传输层协议选择:TCP vs UDP
应用开发者在创建 Socket 时需要选择使用 TCP 还是 UDP。
8.1 TCP 提供的服务
| 服务 | 说明 |
|---|---|
| 面向连接(Connection-oriented) | 通信前需要三次握手(3-way handshake)建立连接 |
| 可靠传输(Reliable Transport) | 保证数据完整、有序到达,丢了会重传 |
| 流量控制(Flow Control) | 发送方不会淹没接收方(根据接收方能力调节速率) |
| 拥塞控制(Congestion Control) | 网络拥塞时自动降速,避免加剧拥塞 |
| 不提供:最低带宽保障、延迟保障、安全性 | 这些需要应用层自行实现(如 TLS 加密) |
8.2 UDP 提供的服务
| 服务 | 说明 |
|---|---|
| 无连接(Connectionless) | 不建立连接,直接发送 |
| 不可靠传输 | 不保证送达、不保证顺序、不重传 |
| 无流量/拥塞控制 | 想发多快就发多快 |
| 轻量高效 | 头部开销小(仅 8 字节 vs TCP 的 20+ 字节),延迟低 |
那 UDP 有什么用? 看起来 UDP 什么都不保证,为什么还有人用?因为 "不保证"换来了"快"和"自由"。对于实时性要求极高的应用(游戏、视频通话),与其等 TCP 重传一个已经过时的数据包,不如直接丢掉继续——用户感觉不到丢了一帧画面,但一定能感觉到 200ms 的延迟卡顿。
8.3 TCP 与 TLS:安全层的加持
TCP 本身不提供加密。TLS(Transport Layer Security) 在 TCP 之上提供:
- 加密:数据在传输中被加密,中间人无法窃听
- 数据完整性:检测数据是否被篡改
- 身份认证:验证服务器(可选客户端)的身份
graph TD
App["应用层数据"] --> TLS["TLS 加密\n(应用层实现,SSL/TLS 库)"]
TLS --> TCP["TCP 可靠传输"]
TCP --> IP["IP 路由"]
TLS 严格来说不是传输层协议,而是在应用层和传输层之间的一个"安全层"。HTTPS = HTTP + TLS。
九、Socket 编程基础
9.1 TCP Socket 编程流程
sequenceDiagram
participant S as 服务器
participant C as 客户端
Note over S: 1. 创建 Socket
Note over S: 2. bind(端口)
Note over S: 3. listen()
Note over C: 1. 创建 Socket
Note over S: 4. accept()(阻塞等待)
C->>S: TCP 三次握手
S-->>C: 连接建立
Note over C: 2. connect() 返回
C->>S: send() 数据
S->>C: send() 数据
Note over S,C: 5/3. recv/send 数据来回传输
Note over S: 6. close()
Note over C: 4. close()
关键区分:服务器有两个 Socket——一个用于
listen等待新连接(欢迎 Socket / welcome socket),accept()返回的是另一个专门用于和这个客户端通信的 连接 Socket(connection socket)。这样服务器可以同时和多个客户端通信。
9.2 UDP Socket 编程流程
sequenceDiagram
participant S as 服务器
participant C as 客户端
Note over S: 1. 创建 Socket
Note over S: 2. bind(端口)
Note over C: 1. 创建 Socket
Note over S: 3. recvfrom()(阻塞等待)
Note over S,C: 无需建立连接!
C->>S: 2. sendto()(附带目的 IP + 端口)
S->>C: 4. sendto()(附带目的 IP + 端口)
Note over C: 3. recvfrom()
Note over S: 5. close()
Note over C: 4. close()
| 对比 | TCP Socket | UDP Socket |
|---|---|---|
| 建立连接 | 需要(connect / accept) | 不需要 |
| 发送数据 | send() — 不需指定目的地(连接已建立) | sendto() — 每次都要指定目的 IP + 端口 |
| 接收数据 | recv() | recvfrom() — 同时获知发送者的地址 |
| 服务器 Socket 数 | 2 个(welcome + connection) | 1 个(所有客户端共用) |
UDP 没有连接的后果:UDP 服务器用同一个 Socket 服务所有客户端。它每次
recvfrom()收到一个数据报时,必须检查"这是从哪个客户端来的"才知道要回复给谁。而 TCP 每个客户端有专属的 connection socket,所以不存在这个问题。
十、Web 与 HTTP 协议
10.1 基本概念
| 概念 | 说明 |
|---|---|
| Web 页面(Web Page) | 由多个对象(object) 组成:HTML 文件、图片、JS、CSS 等 |
| URL | 统一资源定位符,格式:协议://主机名:端口/路径 |
| HTTP | HyperText Transfer Protocol,Web 的应用层协议 |
HTTP 使用 TCP 作为传输协议。客户端发起 TCP 连接(默认端口 80),然后在 TCP 连接上交换 HTTP 报文。
HTTP 是无状态的(Stateless):服务器不保留任何关于客户端之前请求的信息。你刚访问了首页,再访问另一页时服务器并不"记得"你。(后来用 Cookie 弥补了这一点。)
10.2 持久连接 vs 非持久连接
非持久 HTTP(HTTP/1.0):
每个对象需要一个单独的 TCP 连接。一个包含 10 张图片的网页需要建立 11 个 TCP 连接(1 个 HTML + 10 个图片)。
sequenceDiagram
participant C as 客户端
participant S as 服务器
rect rgb(230, 240, 255)
Note over C,S: 对象 1
C->>S: TCP 连接建立(3次握手)
C->>S: HTTP 请求
S->>C: HTTP 响应
C->>S: TCP 连接关闭
end
rect rgb(255, 240, 230)
Note over C,S: 对象 2(又要重来一遍!)
C->>S: TCP 连接建立
C->>S: HTTP 请求
S->>C: HTTP 响应
C->>S: TCP 连接关闭
end
非持久 HTTP 的响应时间:
- 第一个 RTT:TCP 三次握手
- 第二个 RTT:HTTP 请求 + 响应开始
- 然后是实际文件传输
持久 HTTP(HTTP/1.1 默认):
一个 TCP 连接上可以传输多个对象,服务器在发送响应后不关闭连接。
sequenceDiagram
participant C as 客户端
participant S as 服务器
C->>S: TCP 连接建立
rect rgb(230, 255, 230)
Note over C,S: 同一个 TCP 连接!
C->>S: HTTP 请求 1
S->>C: HTTP 响应 1
C->>S: HTTP 请求 2
S->>C: HTTP 响应 2
C->>S: HTTP 请求 3
S->>C: HTTP 响应 3
end
Note over C,S: ...空闲超时后关闭
流水线(Pipelining):HTTP/1.1 还支持客户端不等响应就连续发出多个请求,进一步减少等待时间。但由于实现上的"队头阻塞"问题,实际效果有限,HTTP/2 用多路复用彻底解决了这个问题。
10.3 HTTP 报文格式
请求报文:
GET /index.html HTTP/1.1\r\n ← 请求行(方法 URL 版本)
Host: www.example.com\r\n ← 头部字段
User-Agent: Mozilla/5.0\r\n
Connection: keep-alive\r\n
Accept-Language: zh-CN\r\n
\r\n ← 空行(头部结束标志)
← 消息体(GET 一般没有)
| HTTP 方法 | 用途 | 有消息体? |
|---|---|---|
| GET | 获取资源 | 无 |
| POST | 提交数据(表单、上传) | 有 |
| PUT | 上传/替换资源 | 有 |
| DELETE | 删除资源 | 一般无 |
| HEAD | 只获取头部(不要正文) | 无 |
响应报文:
HTTP/1.1 200 OK\r\n ← 状态行(版本 状态码 短语)
Date: Mon, 30 Mar 2026 08:00:00 GMT\r\n
Content-Type: text/html\r\n
Content-Length: 6821\r\n
Connection: keep-alive\r\n
\r\n
<html>...</html> ← 消息体
| 状态码 | 含义 | 常见场景 |
|---|---|---|
| 200 OK | 请求成功 | 正常返回页面 |
| 301 Moved Permanently | 资源永久移动 | 网站换了域名 |
| 304 Not Modified | 资源未修改(用缓存的) | 浏览器缓存命中 |
| 400 Bad Request | 请求语法错误 | 参数格式不对 |
| 404 Not Found | 资源不存在 | 页面被删了或 URL 打错了 |
| 505 HTTP Version Not Supported | 服务器不支持该 HTTP 版本 | 极少见 |
10.4 Cookie:为无状态的 HTTP 添加状态
HTTP 本身无状态,但很多应用需要"记住用户"。Cookie 机制通过四个组件实现:
sequenceDiagram
participant C as 客户端
participant S as 服务器
rect rgb(230, 240, 255)
Note over C,S: 首次访问
C->>S: GET /index.html
S->>C: HTTP 响应 + Set-Cookie: id=1678
Note over S: 服务器创建 Cookie,存入数据库
Note over C: 浏览器保存 Cookie 到本地
end
rect rgb(255, 245, 230)
Note over C,S: 后续访问
C->>S: GET /page2.html + Cookie: id=1678
Note over S: 查数据库,识别用户
S->>C: 个性化响应
end
| 组件 | 说明 |
|---|---|
响应头 Set-Cookie | 服务器在响应中设置 Cookie |
请求头 Cookie | 客户端在后续请求中携带 Cookie |
| 客户端本地存储 | 浏览器把 Cookie 存在本地文件中 |
| 服务器端数据库 | 服务器根据 Cookie 值查找用户信息 |
隐私问题:Cookie 让网站可以追踪用户行为——你访问了哪些页面、停留了多久、买了什么。这也是为什么现在有 GDPR 等法规要求网站征得用户同意后才能设置追踪 Cookie。
10.5 Web 缓存(代理服务器)
Web 缓存(也叫代理服务器)是代替源服务器响应 HTTP 请求的中间实体。
graph LR
C["客户端"] -->|"① 请求"| Cache["Web 缓存\n(代理服务器)"]
Cache -->|"② 缓存未命中时\n转发请求"| Origin["源服务器"]
Origin -->|"③ 响应"| Cache
Cache -->|"④ 响应\n(命中时直接返回)"| C
| 优势 | 说明 |
|---|---|
| 减少响应时间 | 缓存通常在本地网络,延迟远小于去源服务器 |
| 减少流量 | 不需要每次都跨越 Internet 获取 |
| 减轻服务器负载 | 源服务器不需要处理所有请求 |
条件 GET(Conditional GET):
缓存怎么知道自己存的内容是否过期?通过 If-Modified-Since 头部:
sequenceDiagram
participant Cache as Web 缓存
participant Origin as 源服务器
Cache->>Origin: GET + If-Modified-Since: <上次获取时间>
alt 资源未修改
Origin->>Cache: 304 Not Modified(用缓存的)
else 资源已更新
Origin->>Cache: 200 OK + 新内容
end
~/.cache/google-chrome)
生命周期由 HTTP 响应头控制,可跨会话存活
容量通常几百 MB,浏览器自动淘汰旧的
典型内容图片、字体、JS、CSS 等静态资源
ETag: "abc123def"
Last-Modified: Tue, 14 Apr 2026 10:00:00 GMT
CDN-Cache-Control: max-age=86400 # 只控制 CDN
Surrogate-Control: max-age=3600 # Varnish 等
10.6 缓存策略速记(把图和请求头对应起来)
| 策略 | 关键响应头 | 典型结果 | 适用对象 |
|---|---|---|---|
| 强缓存(Fresh Cache) | Cache-Control: max-age=... / Expires | 未过期时不发请求,直接本地读取 | 指纹静态资源(JS/CSS/图片) |
| 协商缓存(Validation Cache) | ETag + If-None-Match,Last-Modified + If-Modified-Since | 发送条件请求;未变化则 304,变化则 200 + 新内容 | HTML、可能变化的 API 响应 |
| 共享缓存控制 | public / private / s-maxage / Vary | 控制代理/CDN 能否缓存以及缓存多久 | CDN 与反向代理场景 |
| 禁止缓存 | Cache-Control: no-store | 每次都走网络,避免落盘 | 登录态、支付页、敏感信息 |
一次完整的请求旅程
你打开 example.com,浏览器按这个顺序查找资源:
flowchart LR
A[Memory Cache] --> B[Disk Cache] --> C[Service Worker]
C --> D[Forward Proxy] --> E[CDN] --> F[服务端缓存] --> G[数据库]
- 任何一层命中,后面的层都不用再碰。
- 理想状态下,多数请求在 Disk Cache 或 CDN 就被拦截。
- 如果前面命中率稳定,数据库几乎感觉不到这部分流量。
两个最容易混淆的概念
| 概念 | 容易混淆点 | 正确理解 | 风险提示 |
|---|---|---|---|
| Private vs Shared | “都是缓存,应该都能共享” | 浏览器缓存是私有缓存(Private),CDN/代理缓存是共享缓存(Shared) | 带用户信息的页面必须 Cache-Control: private,否则可能发生用户串数据 |
| 验证 vs 直接用 | “过期就一定要重下” | 过期后可先做条件请求验证,不等于内容真的变了 | 配好 ETag / Last-Modified 后,大量请求可走 304 Not Modified,正文 0 字节 |
一个最常见的协商缓存请求长这样:
GET /app.js HTTP/1.1
If-None-Match: "abc123def"
If-Modified-Since: Tue, 14 Apr 2026 10:00:00 GMT
如果资源没变,服务器返回:
HTTP/1.1 304 Not Modified
最容易踩的坑:新版本上线后仍在用旧缓存
这是 CDN 配置里非常常见的事故:代码已经发布新版本,但用户还在命中旧缓存。
最稳的解法是 Cache Busting(内容哈希文件名):
- 把静态资源命名为
main.a3f9c2.js这类带内容哈希的文件名。 - 文件内容一变,哈希就变,URL 随之变化。
- 旧 URL 继续命中旧缓存,新 URL 自动建立新缓存,两者互不干扰。
实践里通常这样配:
- JS/CSS/图片(带哈希文件名):
Cache-Control: public, max-age=31536000, immutable - HTML 入口文档:
Cache-Control: no-cache(每次先验证,拿到最新资源引用)
一个便于自查的顺序:
- 先看 DevTools 的
Size/Status,判断是 memory、disk、304 还是网络直出。 - 再看响应头里
Cache-Control、ETag、Last-Modified、Age。 - 最后看请求头是否带了
If-None-Match或If-Modified-Since,确认是否走协商缓存。
如果要做一个稳妥的站点默认配置,可以从这组思路起步:
- 指纹资源(
app.8f3a1.js):Cache-Control: public, max-age=31536000, immutable - HTML 文档:
Cache-Control: no-cache(允许缓存但每次先验证) - 私有 API(含用户数据):
Cache-Control: private, no-store
十一、HTTP 版本演进
| 版本 | 年份 | 关键特性 |
|---|---|---|
| HTTP/1.0 | 1996 | 非持久连接,每个对象一个 TCP 连接 |
| HTTP/1.1 | 1997 | 持久连接(默认)、流水线、Host 头部 |
| HTTP/2 | 2015 | 二进制分帧、多路复用、服务器推送、头部压缩 |
| HTTP/3 | 2022 | 基于 QUIC(UDP 之上),解决了 TCP 层面的队头阻塞 |
HTTP/2 的多路复用:HTTP/1.1 的流水线虽然允许连续发送请求,但响应必须按顺序返回(队头阻塞)。HTTP/2 把每个请求/响应分成小的"帧",不同请求的帧可以交错传输,彻底解决了应用层的队头阻塞。
HTTP/3 为什么用 UDP? 因为 TCP 的可靠传输机制会导致传输层的队头阻塞——即使只有一个包丢了,TCP 也会阻塞所有后续包直到重传成功。QUIC 基于 UDP,自己在应用层实现可靠传输,每个流独立,一个流丢包不影响其他流。
GETGET /index.html(就这一行!)十二、DNS 体系与解析机制(可视化速记)
十三、关键术语速查
| 英文 | 中文 | 一句话解释 |
|---|---|---|
| Client-Server | 客户端-服务器 | 永远在线的服务器响应客户端的请求 |
| Peer-to-Peer (P2P) | 对等网络 | 没有固定服务器,节点之间直接通信 |
| Self-scalability | 自扩展性 | P2P 中每个新节点既带来需求也带来供给 |
| Socket | 套接字 | 应用进程与传输层之间的 API 接口 |
| Port Number | 端口号 | 标识主机上具体进程的数字 |
| Well-known Port | 知名端口 | 0-1023,保留给标准服务 |
| Stateless | 无状态 | HTTP 服务器不记忆之前的请求 |
| Cookie | Cookie | 为无状态 HTTP 添加用户状态的机制 |
| Persistent Connection | 持久连接 | 一个 TCP 连接传输多个对象 |
| Pipelining | 流水线 | 不等响应就连续发送多个请求 |
| Web Cache / Proxy | Web 缓存/代理 | 代替源服务器响应请求,减少延迟和流量 |
| Conditional GET | 条件 GET | 用 If-Modified-Since 检查缓存是否过期 |
| RTT | 往返时延 | 分组一来一回的时间 |
| TLS | 传输层安全 | 在 TCP 之上提供加密和身份认证 |
| QUIC | 快速 UDP 互联网连接 | 基于 UDP 的传输协议,HTTP/3 的基础 |
| Tit-for-Tat | 以牙还牙 | BitTorrent 的激励机制,上传多才能下载快 |
| Rarest First | 最稀有优先 | BitTorrent 优先下载副本最少的块 |